package com.phiture.erp.purchase.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.phiture.erp.basic.api.pojo.resp.ErpMaterialResp;
import com.phiture.erp.basic.api.service.ErpAccountApi;
import com.phiture.erp.basic.api.service.ErpMaterialApi;
import com.phiture.erp.basic.api.service.ErpSupplierApi;
import com.phiture.erp.common.constants.ErpNoPrefixConstants;
import com.phiture.erp.common.enums.ErpAuditStatus;
import com.phiture.erp.common.utils.ErpNoGenerator;
import com.phiture.erp.purchase.core.pojo.dto.ErpPurchaseOrderPageReqDTO;
import com.phiture.erp.purchase.core.pojo.dto.ErpPurchaseOrderSaveReqDTO;
import com.phiture.erp.purchase.core.service.ErpPurchaseOrderService;
import com.phiture.erp.purchase.core.service.ErpPurchaseRequestService;
import com.phiture.erp.purchase.dal.entity.ErpPurchaseOrderDO;
import com.phiture.erp.purchase.dal.entity.ErpPurchaseOrderItemDO;
import com.phiture.erp.purchase.dal.entity.ErpPurchaseRequestItemsDO;
import com.phiture.erp.purchase.dal.mapper.ErpPurchaseOrderItemMapper;
import com.phiture.erp.purchase.dal.mapper.ErpPurchaseOrderMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static com.phiture.erp.common.constants.ErrorCodeConstants.*;


/**
 * ERP 采购订单 Service 实现类
 */
@Service
@RequiredArgsConstructor
public class ErpPurchaseOrderServiceImpl implements ErpPurchaseOrderService {

    private final ErpPurchaseRequestService purchaseRequestService;
    private final ErpPurchaseOrderMapper purchaseOrderMapper;
    private final ErpPurchaseOrderItemMapper purchaseOrderItemMapper;
    private final ErpNoGenerator erpNoGenerator;
    private final ErpSupplierApi supplierApi;
    private final ErpMaterialApi materialApi;
    private final ErpAccountApi accountApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPurchaseOrder(ErpPurchaseOrderSaveReqDTO createReqDTO) {
        // 1.1 校验订单项的有效性
        List<ErpPurchaseOrderItemDO> purchaseOrderItems = validatePurchaseOrderItems(createReqDTO.getItems());
        // 1.2 校验供应商
        supplierApi.validateSupplier(createReqDTO.getSupplierId());
        // 1.3 校验结算账户
        if (createReqDTO.getAccountId() != null) {
            accountApi.validateAccount(createReqDTO.getAccountId());
        }
        // 1.4 生成订单号，并校验唯一性
        String no = erpNoGenerator.generate(ErpNoPrefixConstants.PURCHASE_ORDER_NO_PREFIX);
        if (purchaseOrderMapper.selectByNo(no) != null) {
            throw exception(PURCHASE_ORDER_NO_EXISTS);
        }

        // 2.1 插入订单
        ErpPurchaseOrderDO purchaseOrder = BeanUtils.toBean(createReqDTO, ErpPurchaseOrderDO.class, in -> in
                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));
        calculateTotalPrice(purchaseOrder, purchaseOrderItems);
        purchaseOrderMapper.insert(purchaseOrder);
        // 2.2 插入订单项
        purchaseOrderItems.forEach(o -> o.setOrderId(purchaseOrder.getId()));
        purchaseOrderItemMapper.insertBatch(purchaseOrderItems);

        // 采购申请单下推的情况反写
        doPurchaseRequestPush(purchaseOrderItems);
        return purchaseOrder.getId();
    }

    private void doPurchaseRequestPush(List<ErpPurchaseOrderItemDO> purchaseOrderItems) {
        purchaseOrderItems.forEach(item -> {
            if(!Objects.isNull(item.getSourceBillId())){
                // 校验采购申请单的已采购数量+本次采购数量是否大于申请数量，如果大于，则抛出异常
                ErpPurchaseRequestItemsDO requestItem = purchaseRequestService.getPurchaseRequestItemsListBId(item.getSourceBillDetailId());
                BigDecimal purchaseOrderQty = requestItem.getPurchaseOrderQty() == null ? BigDecimal.ZERO : requestItem.getPurchaseOrderQty();
                BigDecimal itemCount = item.getCount() == null ? BigDecimal.ZERO : item.getCount();
                BigDecimal requestItemQty = requestItem.getQty() == null ? BigDecimal.ZERO : requestItem.getQty();

                if (purchaseOrderQty.add(itemCount).compareTo(requestItemQty) > 0) {
                    throw exception(PURCHASE_REQUEST_PUSH_FAIL);
                }else {
                    // 更新采购申请单的已采购数量
                    requestItem.setPurchaseOrderQty(purchaseOrderQty.add(itemCount));
                    purchaseRequestService.updatePurchaseRequestItem(BeanUtils.toBean(requestItem, ErpPurchaseRequestItemsDO.class));
                }
            }
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchaseOrder(ErpPurchaseOrderSaveReqDTO updateReqVO) {
        // 1.1 校验存在
        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(updateReqVO.getId());
        if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseOrder.getStatus())) {
            throw exception(PURCHASE_ORDER_UPDATE_FAIL_APPROVE, purchaseOrder.getNo());
        }
        // 1.2 校验供应商
        supplierApi.validateSupplier(updateReqVO.getSupplierId());
        // 1.3 校验结算账户
        if (updateReqVO.getAccountId() != null) {
            accountApi.validateAccount(updateReqVO.getAccountId());
        }
        // 1.4 校验订单项的有效性
        List<ErpPurchaseOrderItemDO> purchaseOrderItems = validatePurchaseOrderItems(updateReqVO.getItems());

        // 2.1 更新订单
        ErpPurchaseOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseOrderDO.class);
        calculateTotalPrice(updateObj, purchaseOrderItems);
        purchaseOrderMapper.updateById(updateObj);

        // 2.2 更新订单项
        updatePurchaseOrderItemList(updateReqVO.getId(), purchaseOrderItems);
    }


    private void doPurchaseRequestPushForUpdate(List<ErpPurchaseOrderItemDO> purchaseOrderItems) {
        purchaseOrderItems.forEach(item -> {
            if(!Objects.isNull(item.getSourceBillId())){
                // 检索出当前采购订单明细行数据库中的信息
                ErpPurchaseOrderItemDO itemDO = purchaseOrderItemMapper.selectById(item.getId());
                // 计算本次采购数量与数据库中的采购数量的差值
                BigDecimal countDiff = item.getCount().subtract(itemDO.getCount());

                // 校验采购申请单的已采购数量+本次采购数量是否大于申请数量，如果大于，则抛出异常
                ErpPurchaseRequestItemsDO requestItem = purchaseRequestService.getPurchaseRequestItemsListBId(item.getSourceBillDetailId());
                BigDecimal purchaseOrderQty = requestItem.getPurchaseOrderQty() == null ? BigDecimal.ZERO : requestItem.getPurchaseOrderQty();
                BigDecimal requestItemQty = requestItem.getQty() == null ? BigDecimal.ZERO : requestItem.getQty();

                if (purchaseOrderQty.add(countDiff).compareTo(requestItemQty) > 0) {
                    throw exception(PURCHASE_REQUEST_PUSH_FAIL);
                }else {
                    // 更新采购申请单的已采购数量
                    requestItem.setPurchaseOrderQty(purchaseOrderQty.add(countDiff));
                    purchaseRequestService.updatePurchaseRequestItem(BeanUtils.toBean(requestItem, ErpPurchaseRequestItemsDO.class));
                }
            }
        });
    }

    private void calculateTotalPrice(ErpPurchaseOrderDO purchaseOrder, List<ErpPurchaseOrderItemDO> purchaseOrderItems) {
        purchaseOrder.setTotalCount(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getCount, BigDecimal::add));
        purchaseOrder.setTotalMaterialPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
        purchaseOrder.setTotalTaxPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));
        purchaseOrder.setTotalPrice(purchaseOrder.getTotalMaterialPrice().add(purchaseOrder.getTotalTaxPrice()));
        // 计算优惠价格
        if (purchaseOrder.getDiscountPercent() == null) {
            purchaseOrder.setDiscountPercent(BigDecimal.ZERO);
        }
        purchaseOrder.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseOrder.getTotalPrice(), purchaseOrder.getDiscountPercent()));
        purchaseOrder.setTotalPrice(purchaseOrder.getTotalPrice().subtract(purchaseOrder.getDiscountPrice()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchaseOrderStatus(Long id, Integer status) {
        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
        // 1.1 校验存在
        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
        // 1.2 校验供应商
        supplierApi.validateSupplier(purchaseOrder.getSupplierId());
        // 1.2 校验状态
        if (purchaseOrder.getStatus().equals(status)) {
            throw exception(approve ? PURCHASE_ORDER_APPROVE_FAIL : PURCHASE_ORDER_PROCESS_FAIL);
        }
        // 1.3 存在采购入单，无法反审核
        if (!approve && purchaseOrder.getInCount().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN);
        }
        // 1.4 存在采购退货单，无法反审核
        if (!approve && purchaseOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN);
        }

        // 2. 更新状态
        int updateCount = purchaseOrderMapper.updateByIdAndStatus(id, purchaseOrder.getStatus(),
                new ErpPurchaseOrderDO().setStatus(status));
        if (updateCount == 0) {
            throw exception(approve ? PURCHASE_ORDER_APPROVE_FAIL : PURCHASE_ORDER_PROCESS_FAIL);
        }
    }

    private List<ErpPurchaseOrderItemDO> validatePurchaseOrderItems(List<ErpPurchaseOrderSaveReqDTO.Item> list) {
        // 1. 校验产品存在
        List<ErpMaterialResp> materialList = materialApi.validMaterialList(
                convertSet(list, ErpPurchaseOrderSaveReqDTO.Item::getMaterialId));
        Map<Long, ErpMaterialResp> materialMap = convertMap(materialList, ErpMaterialResp::getId);
        // 2. 转化为 ErpPurchaseOrderItemDO 列表
        return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseOrderItemDO.class, item -> {
            item.setMaterialUnitId(materialMap.get(item.getMaterialId()).getUnitId());
            item.setTotalPrice(MoneyUtils.priceMultiply(item.getMaterialPrice(), item.getCount()));
            if (item.getTotalPrice() == null) {
                return;
            }
            if (item.getTaxPercent() != null) {
                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
            }
        }));
    }

    private void updatePurchaseOrderItemList(Long id, List<ErpPurchaseOrderItemDO> newList) {
        // 第一步，对比新老数据，获得添加、修改、删除的列表
        List<ErpPurchaseOrderItemDO> oldList = purchaseOrderItemMapper.selectListByOrderId(id);
        List<List<ErpPurchaseOrderItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录
                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));

        // 第二步，批量添加、修改、删除
        if (CollUtil.isNotEmpty(diffList.get(0))) {
            diffList.get(0).forEach(o -> o.setOrderId(id));
            purchaseOrderItemMapper.insertBatch(diffList.get(0));
            // 采购申请单下推的情况反写
            doPurchaseRequestPush(diffList.get(0));
        }
        if (CollUtil.isNotEmpty(diffList.get(1))) {
            // 采购申请单下推的情况反写
            doPurchaseRequestPushForUpdate(diffList.get(1));
            purchaseOrderItemMapper.updateBatch(diffList.get(1));
        }
        if (CollUtil.isNotEmpty(diffList.get(2))) {
            purchaseOrderItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpPurchaseOrderItemDO::getId));
            // 把删除的采购订单明细的数量取反
            diffList.get(2).forEach(o -> o.setCount(o.getCount().negate()));
            // 采购申请单下推的情况反写
            doPurchaseRequestPush(diffList.get(2));
        }
    }

    @Override
    public void updatePurchaseOrderInCount(Long id, Map<Long, BigDecimal> inCountMap) {
        List<ErpPurchaseOrderItemDO> orderItems = purchaseOrderItemMapper.selectListByOrderId(id);
        // 1. 更新每个采购订单项
        orderItems.forEach(item -> {
            BigDecimal inCount = inCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);
//            if (item.getInCount().equals(inCount)) {
//                return;
//            }
            if (inCount.compareTo(item.getCount().add(item.getReturnCount())) > 0) {
                throw exception(PURCHASE_ORDER_ITEM_IN_FAIL_PRODUCT_EXCEED,
                        materialApi.getMaterial(item.getMaterialId()).getName(), item.getCount());
            }
            purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setInCount(inCount));
        });
        // 2. 更新采购订单
        BigDecimal totalInCount = getSumValue(inCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);
        purchaseOrderMapper.updateById(new ErpPurchaseOrderDO().setId(id).setInCount(totalInCount));
    }

    @Override
    public void updatePurchaseOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap) {
        List<ErpPurchaseOrderItemDO> orderItems = purchaseOrderItemMapper.selectListByOrderId(orderId);
        // 1. 更新每个采购订单项
        orderItems.forEach(item -> {
            BigDecimal returnCount = returnCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);
            if (item.getReturnCount().equals(returnCount)) {
                return;
            }
            if (returnCount.compareTo(item.getInCount()) > 0) {
                throw exception(PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED,
                        materialApi.getMaterial(item.getMaterialId()).getName(), item.getInCount());
            }
            purchaseOrderItemMapper.updateById(new ErpPurchaseOrderItemDO().setId(item.getId()).setReturnCount(returnCount));
        });
        // 2. 更新采购订单
        BigDecimal totalReturnCount = getSumValue(returnCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);
        purchaseOrderMapper.updateById(new ErpPurchaseOrderDO().setId(orderId).setReturnCount(totalReturnCount));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePurchaseOrder(List<Long> ids) {
        // 1. 校验不处于已审批
        List<ErpPurchaseOrderDO> purchaseOrders = purchaseOrderMapper.selectBatchIds(ids);
        if (CollUtil.isEmpty(purchaseOrders)) {
            return;
        }
        purchaseOrders.forEach(purchaseOrder -> {
            if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseOrder.getStatus())) {
                throw exception(PURCHASE_ORDER_DELETE_FAIL_APPROVE, purchaseOrder.getNo());
            }
        });

        // 2. 遍历删除，并记录操作日志
        purchaseOrders.forEach(purchaseOrder -> {
            // 2.1 删除订单
            purchaseOrderMapper.deleteById(purchaseOrder.getId());

            //  采购申请单下推的情况反写
            List<ErpPurchaseOrderItemDO> orderItems = purchaseOrderItemMapper.selectListByOrderId(purchaseOrder.getId());
            if (CollUtil.isNotEmpty(orderItems)) {
                orderItems.forEach(o -> o.setCount(o.getCount().negate()));
                doPurchaseRequestPush(orderItems);
            }
            // 2.2 删除订单项
            purchaseOrderItemMapper.deleteByOrderId(purchaseOrder.getId());
        });
    }

    private ErpPurchaseOrderDO validatePurchaseOrderExists(Long id) {
        ErpPurchaseOrderDO purchaseOrder = purchaseOrderMapper.selectById(id);
        if (purchaseOrder == null) {
            throw exception(PURCHASE_ORDER_NOT_EXISTS);
        }
//        if(purchaseOrder.getSupplierId() == null){
//            throw exception(PURCHASE_ORDER_NOT_SUPPLIER);
//        }
        return purchaseOrder;
    }

    @Override
    public ErpPurchaseOrderDO getPurchaseOrder(Long id) {
        return purchaseOrderMapper.selectById(id);
    }

    @Override
    public ErpPurchaseOrderDO getPurchaseOrderByPurNo(String purNo) {
        // 增加一个查询条件，总数>入库数量
        purchaseOrderMapper.selectOne(new LambdaUpdateWrapper<ErpPurchaseOrderDO>().eq(ErpPurchaseOrderDO::getNo, purNo)
                        .apply("total_count > (in_count - return_count) ")
                );
        return purchaseOrderMapper.selectByNo(purNo);
    }

    @Override
    public ErpPurchaseOrderDO validatePurchaseOrder(Long id) {
        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
        if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
            throw exception(PURCHASE_ORDER_NOT_APPROVE);
        }
        return purchaseOrder;
    }

    @Override
    public PageResult<ErpPurchaseOrderDO> getPurchaseOrderPage(ErpPurchaseOrderPageReqDTO pageReqDTO) {
        MPJLambdaWrapperX<ErpPurchaseOrderDO> query = new MPJLambdaWrapperX<ErpPurchaseOrderDO>()
                .likeIfPresent(ErpPurchaseOrderDO::getNo, pageReqDTO.getNo())
                .eqIfPresent(ErpPurchaseOrderDO::getSupplierId, pageReqDTO.getSupplierId())
                .betweenIfPresent(ErpPurchaseOrderDO::getOrderTime, pageReqDTO.getOrderTime())
                .eqIfPresent(ErpPurchaseOrderDO::getStatus, pageReqDTO.getStatus())
                .likeIfPresent(ErpPurchaseOrderDO::getRemark, pageReqDTO.getRemark())
                .eqIfPresent(ErpPurchaseOrderDO::getCreator, pageReqDTO.getCreator())
                .orderByDesc(ErpPurchaseOrderDO::getId);
        // 入库状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报 in_count 错误
        if (Objects.equals(pageReqDTO.getInStatus(), ErpPurchaseOrderPageReqDTO.IN_STATUS_NONE)) {
            query.eq(ErpPurchaseOrderDO::getInCount, 0);
        } else if (Objects.equals(pageReqDTO.getInStatus(), ErpPurchaseOrderPageReqDTO.IN_STATUS_PART)) {
            query.gt(ErpPurchaseOrderDO::getInCount, 0).apply("t.in_count < t.total_count");
        } else if (Objects.equals(pageReqDTO.getInStatus(), ErpPurchaseOrderPageReqDTO.IN_STATUS_ALL)) {
            query.apply("t.in_count = t.total_count");
        }
        // 退货状态
        if (Objects.equals(pageReqDTO.getReturnStatus(), ErpPurchaseOrderPageReqDTO.RETURN_STATUS_NONE)) {
            query.eq(ErpPurchaseOrderDO::getReturnCount, 0);
        } else if (Objects.equals(pageReqDTO.getReturnStatus(), ErpPurchaseOrderPageReqDTO.RETURN_STATUS_PART)) {
            query.gt(ErpPurchaseOrderDO::getReturnCount, 0).apply("t.return_count < t.total_count");
        } else if (Objects.equals(pageReqDTO.getReturnStatus(), ErpPurchaseOrderPageReqDTO.RETURN_STATUS_ALL)) {
            query.apply("t.return_count = t.total_count");
        }
        // 可采购入库
        if (Boolean.TRUE.equals(pageReqDTO.getInEnable())) {
            query.eq(ErpPurchaseOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
                    .apply("t.in_count < (t.total_count + t.return_count)");
        }
        // 可采购退货
        if (Boolean.TRUE.equals(pageReqDTO.getReturnEnable())) {
            query.eq(ErpPurchaseOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
                    .apply("t.return_count < t.in_count");
        }
        if (pageReqDTO.getMaterialId() != null) {
            query.leftJoin(ErpPurchaseOrderItemDO.class, ErpPurchaseOrderItemDO::getOrderId, ErpPurchaseOrderDO::getId)
                    .eq(pageReqDTO.getMaterialId() != null, ErpPurchaseOrderItemDO::getMaterialId, pageReqDTO.getMaterialId())
                    .groupBy(ErpPurchaseOrderDO::getId); // 避免 1 对多查询，产生相同的 1
        }
        return purchaseOrderMapper.selectJoinPage(pageReqDTO, ErpPurchaseOrderDO.class, query);
    }

    // ==================== 订单项 ====================

    @Override
    public List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderId(Long orderId) {
        return purchaseOrderItemMapper.selectListByOrderId( orderId);
    }

    @Override
    public List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderIdForPurIn(Long orderId) {
        return purchaseOrderItemMapper.selectListListByOrderIdForPurIn( orderId);
    }

    @Override
    public List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderIds(Collection<Long> orderIds) {
        if (CollUtil.isEmpty(orderIds)) {
            return Collections.emptyList();
        }
        return purchaseOrderItemMapper.selectListByOrderIds(orderIds);
    }

    @Override
    public void createErpPurchaseOrder(ErpPurchaseOrderDO orderDO) {
        purchaseOrderMapper.insert(orderDO);
    }

    @Override
    public void createErpPurchaseOrderItem(ErpPurchaseOrderItemDO orderItemDO) {
        purchaseOrderItemMapper.insert(orderItemDO);
    }

}